#include "kernel.h"
#include <assert.h>
#include <string>
#include <sstream>
#include <fstream>
#include "ErrorCheck.h"



Kernel::Kernel(unsigned width, unsigned height, TexParam &param, const char *shaderFile)
: width(width), height(height), param(param)
{
	readIndex = 0;
	writeIndex = 1;

	initCG(shaderFile);
	initData();
}
Kernel::~Kernel()
{
}
void
Kernel::initData()
{
	glGenTextures(1, &bodiesTexID);
	glGenTextures(2, accelsTexID);

	createTexture(bodiesTexID);
	createTexture(accelsTexID[readIndex]);
	createTexture(accelsTexID[writeIndex]);
}
void
Kernel::calc(float *bodies, float *accels, float eps)
{
	sendToTexture(bodiesTexID, bodies);

	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attach[writeIndex],
		param.target, accelsTexID[writeIndex], 0);
	glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attach[readIndex],
		param.target, accelsTexID[readIndex], 0);

	cgGLEnableProfile(profile);
	cgGLBindProgram(program);
	// enable texture parameters
	cgGLSetTextureParameter(bodiesParam, bodiesTexID);
	cgGLEnableTextureParameter(bodiesParam);
	// enable scalar parameters
	cgGLSetParameter1f(epsParam, eps);
	cgGLSetParameter2f(sizeParam, width, height);

	//glFinish();

	//for (unsigned yBlock = 0; yBlock < nParallel; yBlock++) {
		//cgGLSetParameter1f(blockParam, yBlock);

		glDrawBuffer(attach[writeIndex]);
		cgGLSetTextureParameter(accelsParam, accelsTexID[readIndex]);
		cgGLEnableTextureParameter(accelsParam);

		glBegin(GL_QUADS);
		glTexCoord2f(0.0, 0.0); 
		glVertex2f(0.0, 0.0);
		glTexCoord2f(width, 0.0); 
		glVertex2f(width, 0.0);
		glTexCoord2f(width, height); 
		glVertex2f(width, height);
		glTexCoord2f(0.0, height); 
		glVertex2f(0.0, height);
		glEnd();

		swap();
		//glFlush();
		glFinish();
	//}

	//glFinish();
	getFromTexture(accelsTexID[readIndex], accels);
	//glFinish();
}
void
Kernel::swap()
{
	int tmp;
		
	tmp = readIndex;
	readIndex = writeIndex;
	writeIndex = tmp;
}
void
Kernel::initCG(const char *shaderFile)
{
	CGenum fileType = CG_OBJECT;

	context = cgCreateContext();
	profile = cgGLGetLatestProfile(CG_GL_FRAGMENT);
	cgGLSetOptimalOptions(profile);
	program = cgCreateProgram(context, fileType, loadShader(shaderFile).c_str(),
		profile, "main", 0);
	cgGLLoadProgram(program);
	accelsParam = cgGetNamedParameter(program, "accelsTexture");
	bodiesParam = cgGetNamedParameter(program, "bodiesTexture");
	epsParam = cgGetNamedParameter(program, "eps");
	sizeParam = cgGetNamedParameter(program, "size");
}
void
Kernel::createTexture(GLuint texID)
{
    glBindTexture(param.target, texID);
    glTexParameteri(param.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(param.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(param.target, GL_TEXTURE_WRAP_S, GL_CLAMP);
    glTexParameteri(param.target, GL_TEXTURE_WRAP_T, GL_CLAMP);

	glTexImage2D(param.target, 0, param.iformat, 
		width, height, 0, param.format, GL_FLOAT, 0);
}
void
Kernel::sendToTexture(GLuint texID, float *data)
{
    glBindTexture(param.target, texID);
    glTexSubImage2D(param.target, 0, 0, 0, 
		width, height, 
		param.format, GL_FLOAT, data);

}
void
Kernel::getFromTexture(GLuint texID, float *data)
{
	glReadBuffer(attach[readIndex]);
    glReadPixels(0, 0, width, height, 
		param.format, GL_FLOAT, data);
}
std::string
Kernel::loadShader(const char *shaderFile)
{
	std::ifstream input(shaderFile, std::fstream::binary);
	std::stringstream shader;

	assert(input.is_open());
	while (true) {
		char buf;
		input.get(buf);
		if (input.eof())
			break;
		shader << buf;
	}
	return shader.str();
}
const GLenum
Kernel::attach[] = {
	GL_COLOR_ATTACHMENT0_EXT,
	GL_COLOR_ATTACHMENT1_EXT
};
